package io.gitee.caoxiaoyu97.lifecycle.lifecyle;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 * 优雅停机出现部分异常；解决两个问题：
 * 1. 更换health 接口作为探活，重启后避免组件创建不及时的异常
 * 2. 更换health 对下线的敏感，停机时优先终止心跳，避免过快销毁异常
 * <p>
 * By nixian,HUNDSUN
 */
@Component
public class SmartHealthIndicator implements HealthIndicator, ApplicationListener<ApplicationEvent> {

    private static final Logger logger = LoggerFactory.getLogger(SmartHealthIndicator.class);
    public static final String DOWN_KEY_WORD = "Application is shutting down By SmartHealthIndicator";
    public static final String START_KEY_WORD = "Application is runing By SmartHealthIndicator";
    private volatile boolean running = false;

    @Autowired
    Collection<BftsSmartLifecycle> bftsSmartLifecycles;

    @Value("${smart-health-indicator.stop.await.time:0}")
    private int stopAwaitTime;


    @Override
    public Health health() {
        if (!running) {
            return Health.down().withDetail("Reason", "Application is shutting down").build();
        }
        return Health.up().build();
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationReadyEvent) {
            logger.info(START_KEY_WORD);
            for (BftsSmartLifecycle server : bftsSmartLifecycles)
                if (!server.isStarted()) {
                    server.start();
                }
            running = true;
        }
        if (event instanceof ContextClosedEvent) {
            logger.info(DOWN_KEY_WORD);
            running = false;
            try {
                Thread.sleep(stopAwaitTime * 1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            for (BftsSmartLifecycle server : bftsSmartLifecycles) {
                if (server.isStarted()) {
                    server.stop();
                }
            }
        }
    }
}
